//
//  SpatialView.m
//  geom
//
//  Created by 徐洋 on 2018/8/30.
//  Copyright © 2018年 徐洋. All rights reserved.
//

#import "SpatialView.h"

#include <sqlite3.h>
#include "gaiageo.h"
#include "spatialite.h"

@interface SpatialView()

@property (nonatomic, assign) double zoomFactor;
@property (nonatomic, assign) CGPoint originLocation;

@property (nonatomic, assign) double oldZoomFactor;
@property (nonatomic, assign) CGPoint oldOriginLocation;

@property (nonatomic, assign) BOOL isMoving;

@end

#define DEFAULT_ZOOM    0.00011088954174884324f
#define DEFAULT_MIN_X   6983828.2577507077f
#define DEFAULT_MIN_Y   7809037.1986640859f

@implementation SpatialView

- (void)resetPosition {
    self.zoomFactor = DEFAULT_ZOOM;
    self.originLocation = CGPointMake(DEFAULT_MIN_X, DEFAULT_MIN_Y);
    [self refresh];
    [self savePosition];
}


-(void)initialize {
    [self loadPosition];
    
    _isMoving = NO;
    
    UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onPanView:)];
    
    UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapView:)];
    tap.numberOfTapsRequired = 2;
    
    UIPinchGestureRecognizer * pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(onPinchView:)];
    
    [self addGestureRecognizer:pinch];
    [self addGestureRecognizer:pan];
    [self addGestureRecognizer:tap];
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self initialize];
    }
    return self;
}

- (void)savePosition {
    NSUserDefaults * d = [NSUserDefaults standardUserDefaults];
    
    [d setDouble:self.zoomFactor forKey:@"zoom"];
    [d setObject:NSStringFromCGPoint(self.originLocation) forKey:@"origin"];
}

- (void)loadPosition {
    NSUserDefaults * d = [NSUserDefaults standardUserDefaults];
    
    id v = [d objectForKey:@"zoom"];
    self.zoomFactor = v ? [v doubleValue] : DEFAULT_ZOOM;
    
    NSString * s = [d stringForKey:@"origin"];
    self.originLocation = s ? CGPointFromString(s) : CGPointMake(DEFAULT_MIN_X, DEFAULT_MIN_Y);
}

- (void)layoutSubviews {
    [super layoutSubviews];
    
    [self setNeedsDisplay];
}

// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
    if (!self.cache) {
        return;
    }
    
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    
    [self drawAirspaceLayerInContext:ctx];
    if (!self.isMoving) {
        [self drawRestrictedAreasLayerInContext:ctx];
        [self drawAirwaySegmentLayerInContext:ctx];
    }
    [self drawAirportLayerInContext:ctx];

}

- (void)drawAirspaceLayerInContext:(CGContextRef)ctx{
    CGContextSetRGBFillColor(ctx, 1, 0.9f, 0.9f, 1.0f);
    CGContextSetRGBStrokeColor(ctx, 1, 0.5f, 0.5f, 1);

    [self.cache.arrAirspaces enumerateObjectsUsingBlock:^(SpatialObject * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        gaiaPolygonPtr pg = obj.geometry->FirstPolygon;
        while (pg) {
            gaiaRingPtr ring = pg->Exterior;
            double x,y;
            CGPoint * pt = malloc(sizeof(CGPoint) * (ring->Points));
            for (int i=0; i<ring->Points; i++) {
                gaiaGetPoint(ring->Coords, i, &x, &y);
                pt[i] = [self screenPointFromCoordinateX:x Y:y];
            }
            
            // stroke polygon
            CGContextAddLines(ctx, pt, ring->Points);
            CGContextClosePath(ctx);
            CGContextStrokePath(ctx);
            
            // fill polygon
//            CGContextAddLines(ctx, pt, ring->Points);
//            CGContextFillPath(ctx);
            free(pt);
            pg = pg->Next;
        }
        

    }];
}

- (void)drawRestrictedAreasLayerInContext:(CGContextRef)ctx {
    CGContextSetRGBFillColor(ctx, 0.8, 0.8f, 1.0f, 1.0f);
    CGContextSetRGBStrokeColor(ctx, 0, 0.0f, 1.0f, 1);
    
    [self.cache.arrRestrictedAreas enumerateObjectsUsingBlock:^(SpatialObject * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        gaiaPolygonPtr pg = obj.geometry->FirstPolygon;
        while (pg) {
            gaiaRingPtr ring = pg->Exterior;
            double x,y;
            CGPoint * pt = malloc(sizeof(CGPoint) * (ring->Points));
            for (int i=0; i<ring->Points; i++) {
                gaiaGetPoint(ring->Coords, i, &x, &y);
                pt[i] = [self screenPointFromCoordinateX:x Y:y];
            }
            
            // stroke polygon
            CGContextAddLines(ctx, pt, ring->Points);
            CGContextClosePath(ctx);
            CGContextStrokePath(ctx);
            
            // fill polygon
            CGContextAddLines(ctx, pt, ring->Points);
            CGContextFillPath(ctx);

            free(pt);
            pg = pg->Next;
        }
        
        
    }];
}

- (void)drawAirportLayerInContext:(CGContextRef)ctx{
    CGContextSetRGBStrokeColor(ctx, 0.5, 0.5f, 1.0f, 1);
    [self.cache.arrAirports enumerateObjectsUsingBlock:^(SpatialObject * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        gaiaPointPtr pt = obj.geometry->FirstPoint;
        if (pt) {
            CGPoint point = [self screenPointFromCoordinateX:pt->X Y:pt->Y];
            CGContextMoveToPoint(ctx, point.x, point.y);
            CGContextAddArc(ctx, point.x, point.y, 2, 0, 7, 0);
        }
    }];
    CGContextStrokePath(ctx);
}

- (void)drawAirwaySegmentLayerInContext:(CGContextRef)ctx {
    CGContextSetRGBStrokeColor(ctx, 0.5, 1.0f, 0.5f, 1);
    [self.cache.arrAirwaySegments enumerateObjectsUsingBlock:^(SpatialObject * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        gaiaLinestringPtr ls = obj.geometry->FirstLinestring;
        while (ls) {
            double x,y;
            gaiaGetPoint(ls->Coords, 0, &x, &y);
            CGPoint pt = [self screenPointFromCoordinateX:x Y:y];
            CGContextMoveToPoint(ctx, pt.x, pt.y);
            for (int i=0; i<ls->Points; i++) {
                gaiaGetPoint(ls->Coords, i, &x, &y);
                CGPoint pt = [self screenPointFromCoordinateX:x Y:y];
                CGContextAddLineToPoint(ctx, pt.x, pt.y);
            }
            
            ls = ls->Next;
        }
        
        CGContextStrokePath(ctx);
    }];
}

- (CGPoint)screenPointFromCoordinateX:(double)x Y:(double)y {
    x = (x - self.originLocation.x) * self.zoomFactor;
    y = (y - self.originLocation.y) * (-self.zoomFactor);
    
    return CGPointMake(x, y);
}

- (void)refresh {
    float dx = CGRectGetWidth(self.frame) / self.zoomFactor;
    float dy = -CGRectGetHeight(self.frame) / self.zoomFactor;
    
    CGRect rect;
    rect.origin = self.originLocation;
    rect.size = CGSizeMake(dx, dy);

    [self.cache setBounds:rect];
    [self.cache refreshCache];
    [self setNeedsDisplay];
}

- (void)onPanView:(UIPanGestureRecognizer *)pan {
    
    if (pan.state == UIGestureRecognizerStateBegan) {
        self.oldOriginLocation = self.originLocation;
        self.isMoving = YES;
    } else if(pan.state == UIGestureRecognizerStateEnded) {
        self.isMoving = NO;
        [self refresh];

        [self savePosition];
    } else if (pan.state == UIGestureRecognizerStateChanged) {
        CGPoint pt = [pan translationInView:self];
        
        _originLocation.x = self.oldOriginLocation.x - pt.x / self.zoomFactor;
        _originLocation.y = self.oldOriginLocation.y + pt.y / self.zoomFactor;

        [self refresh];
    }
}

- (void)onTapView:(UITapGestureRecognizer *)tap {
    
    CGPoint pt = [tap locationInView:self];
    CGFloat scale = 1.5;
    


    self.oldZoomFactor = self.zoomFactor;
    self.oldOriginLocation = self.originLocation;
    if (tap.numberOfTouches == 1) {
        [self zoomWithScale:scale atPoint:pt];
        [self refresh];
    }
}

- (void)onPinchView:(UIPinchGestureRecognizer *)pinch {
    CGPoint pt = [pinch locationInView:self];
    CGFloat scale = [pinch scale];
    
    if (pinch.state == UIGestureRecognizerStateBegan) {
        self.oldZoomFactor = self.zoomFactor;
        self.oldOriginLocation = self.originLocation;
        self.isMoving = YES;
    } else if(pinch.state == UIGestureRecognizerStateChanged) {
        [self zoomWithScale:scale atPoint:pt];
        [self refresh];
    } else if (pinch.state == UIGestureRecognizerStateEnded) {
        self.isMoving = NO;
        [self zoomWithScale:scale atPoint:pt];
        [self refresh];
        
        [self savePosition];
    }
}

- (void)zoomWithScale:(CGFloat)scale atPoint:(CGPoint)point {
    self.zoomFactor = self.oldZoomFactor * scale;
    double pinchX = point.x / self.oldZoomFactor + self.oldOriginLocation.x;
    double pinchY = -point.y / self.oldZoomFactor + self.oldOriginLocation.y;
    
    _originLocation.x = self.oldOriginLocation.x + (pinchX - self.oldOriginLocation.x) * (1-1/scale);
    _originLocation.y = self.oldOriginLocation.y + (pinchY - self.oldOriginLocation.y) * (1-1/scale);
}

@end
